package com.gmail.dengtao.joe.commons.crypto.cipher;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * Encrypt/decrypt input using the RSA Cipher.
 * @author <a href="mailto:joe.dengtao@gmail.com">DengTao</a>
 * @version 1.0
 * @since 1.0
 */
public class RSA {

    public static byte[] encrypt(byte[] data, byte[] key) throws Exception {
		X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(key);
		PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(bobPubKeySpec);
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, pubKey);
		
		InputStream is = new ByteArrayInputStream(data);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
			byte[] buf = new byte[64];
			int buf_len;
			while ((buf_len = is.read(buf)) != -1) {
				byte[] block = null;
				if (buf.length == buf_len) {
					block = buf;
				} else {
					block = new byte[buf_len];
					for (int i = 0; i < buf_len; i++) {
						block[i] = buf[i];
					}
				}
				bos.write(cipher.doFinal(block));
			}
			return bos.toByteArray();
        } catch (IOException e) {
			throw e;
		} finally {
			if (bos != null) try { bos.close(); } catch (IOException e) { }
			if (is != null) try { is.close(); } catch (IOException e) { }
		}
		
    }

    public static boolean verify(byte[] data, byte[] signed, byte[] key) throws Exception {
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(key));
        Signature signature = Signature.getInstance("SHA1WithRSA");
		signature.initVerify(pubKey);
		signature.update( data );
		return signature.verify( signed );
	}
	
	public static byte[] sign(byte[] data, byte[] key) throws Exception {
		PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec( key ); 
    	KeyFactory keyf = KeyFactory.getInstance("RSA");
    	PrivateKey priKey = keyf.generatePrivate(priPKCS8);
        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initSign(priKey);
        signature.update( data );
        return signature.sign();
	}
	
    public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
    	PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
		Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        InputStream is = new ByteArrayInputStream(data);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
			byte[] buf = new byte[128];
			int buf_len;
			while ((buf_len = is.read(buf)) != -1) {
				byte[] block = null;
				if (buf.length == buf_len) {
					block = buf;
				} else {
					block = new byte[buf_len];
					for (int i = 0; i < buf_len; i++) {
						block[i] = buf[i];
					}
				}
				bos.write(cipher.doFinal(block));
			}
			return bos.toByteArray();
        } catch (IOException e) {
			throw e;
		} finally {
			if (bos != null) try { bos.close(); } catch (IOException e) { }
			if (is != null) try { is.close(); } catch (IOException e) { }
		}
    }

}